//============================================================================
// Name        : TestEstructurado.cpp
// Author      : Daniel Spacek
// Version     :
// Copyright   :
// Description : Hello World in C++, Ansi-style
//============================================================================

#define BLOCKSIZE 128
#define LASTBLOCKSIZE 9

#include <iostream>
#include <fstream>

#include "mtf"
#include "bs"
#include "DescompresorEstructurado"
#include "CompresorEstructurado"
#include "bitstream"

using namespace std;

int compresion(string filename);
int compresion(string inFile, string outFile);
int descompresion(string filename);
int descompresion(string compressFile, string outFileName);
int compresionMTF(string inFileName, string outFileName);

int compresionCompleta(string inFile, string outFile);
int descompresionCompleta(string compressFile, string outFileName);


int main() {

	int i;
	string fileNameOriginal = "hgignore.tmp";
	string fileNameMTF = "MTF_enorme.tmp";
	string fileNameComprimido = "hgignore.xx";
	string fileNameDescomprimido = "hgignore.tmp.des";

	//i = compresionMTF(fileNameOriginal,fileNameMTF);
	//i = compresion(fileNameMTF,fileNameComprimido);
	//i = descompresion(fileNameComprimido,fileNameDescomprimido);

	i = compresionCompleta(fileNameOriginal,fileNameComprimido);
	//i = descompresionCompleta(fileNameComprimido,fileNameDescomprimido);

	string fileName = "MTF.tmp";
	//i = compresion(fileName);
	//i = descompresion(fileName);

	return i;
}

int descompresion(string compressFileName, string outFileName) {

	// Leo el archivo
	ifstream zipFile(compressFileName);
	ibitstream is(&zipFile);

	DescompresorEstructurado ce(&is);

	ofstream outFile(outFileName);
	obitstream os(&outFile);

	streamsize s;
	uchar block[BLOCKSIZE];
	int cont = 0;
/*	uint i = 0;

	mxf * the_mff = new mff();
*/
	while(zipFile.good()) {
		cont++;
		//is.read((char*)&i, sizeof(i));				// leo el indice del BS

		s = ce.descomprimir(block, BLOCKSIZE);		// Estructurado

//		(*the_mff)(block, s);						// MTF

//		debs(block, i, s);							// BS
		//printf("Bloque: %d\n",cont);
		outFile.write((char*)block, s);				// Bloque descomprimido
	}

	zipFile.close();
	outFile.close();

//	delete the_mff;

	cout << "¡¡¡Descomprimido!!!" << endl;

	return 0;
}

int compresion(string inFileName, string outFileName) {

	ofstream outFile(outFileName);
	obitstream os(&outFile);

	CompresorEstructurado ce(&os);

	streamsize s;
	uchar block[BLOCKSIZE];
/*	uint i = 0;

	mxf * the_mtf = new mtf();
*/
	// Leo el archivo
	ifstream inFile(inFileName);
	ibitstream is(&inFile);

	while(inFile.good()) {
		inFile.read((char*)block, BLOCKSIZE);		// Leo el bloque del archivo
		s = inFile.gcount();						// Obtengo el tamanio del bloque

//		i = bs(block, s);							// BS, indice

//		(*the_mtf)(block, s);						// MTF

//		os.write((char*)&i, sizeof(i));				// Guardo el indice del BS

		ce.comprimirBloque(block, s);				// Comprimo el bloque
//		ce.finalizarBloque();						// Como guardo los indices del BS entre bloque y bloque, entonces tengo que cerrarlo
	}
	ce.finalizarArchivo(true);						// Finalizo archivo, como en la linea anterior finalice bloque, entonces param = false

	os.pad();

	outFile.close();
	inFile.close();

	cout << "¡¡¡Comprimido!!!" << endl;

	return 0;
}


int descompresionCompleta(string compressFileName, string outFileName) {

	// Leo el archivo
	ifstream zipFile(compressFileName);
	ibitstream is(&zipFile);

	DescompresorEstructurado de(&is);

	ofstream outFile(outFileName);
	obitstream os(&outFile);

	uint acum = 0;						// cantidad de bloques
	streamsize s = BLOCKSIZE;
	uchar block[BLOCKSIZE];
	uint cont = 0;
	uint i = 0;

	mxf * the_mff = new mff();

	// Leo la cantidad de bloques
	if (zipFile.good()) zipFile.read((char*)&acum,sizeof(acum));

	// Bucle para almacenar los indices del BS
	// Creo el array
	uint indexBS[acum];

	for(cont=0;cont<acum;cont++){
		// Leo el archivo
		zipFile.read((char*)&i, sizeof(i));				// leo el indice del BS
		indexBS[cont] = i;
	}

	// reseteo cont
	cont = 0;

	while(zipFile.good() && (s == BLOCKSIZE)) {		// acá comparo s con BLOCKSIZE porque al descomprimi hay 7 u 8 bits que no los lee el descompreso, entonces el zipFile piensa que tiene que seguir leyendo

		//is.read((char*)&i, sizeof(i));				// leo el indice del BS
		i = indexBS[cont];
		cont++;

		s = de.descomprimir(block, BLOCKSIZE);		// Estructurado

		(*the_mff)(block, s);						// MTF

		debs(block, i, s);							// BS

		outFile.write((char*)block, s);				// Bloque descomprimido
	}

	zipFile.close();
	outFile.close();

	delete the_mff;

	cout << "¡¡¡Descomprimido!!!" << endl;

	return 0;
}

int compresionCompleta(string inFileName, string outFileName) {

	// Archivo temporal del estructurado
	ofstream tmpEstrucFile("struct.tmp");
	obitstream tmpEstruc(&tmpEstrucFile);
	CompresorEstructurado ce(&tmpEstruc);

	// Archivo temporal de los indices del BS
	ofstream tmpBSFile("BS.tmp");
	obitstream tmpBS(&tmpBSFile);

	uint acum = 0;
	streamsize s;
	uchar block[BLOCKSIZE];
	uint i = 0;

	mxf * the_mtf = new mtf();

	// Archivo de origen
	ifstream inFile(inFileName);
	ibitstream is(&inFile);

	while(inFile.good()) {

		inFile.read((char*)block, BLOCKSIZE);		// Leo el bloque del archivo
		acum++;										// Cuento la cantidad de bloques

		s = inFile.gcount();						// Obtengo el tamanio del bloque

		i = bs(block, s);							// BS, indice

		(*the_mtf)(block, s);						// MTF

		//os.write((char*)&i, sizeof(i));			// Guardo el indice del BS
		tmpBS.write((char*)&i, sizeof(i));			// Guardo el indice del BS en archivo temporal

		ce.comprimirBloque(block, s);				// Comprimo el bloque
		// ce.finalizarBloque();					//// No hace falta
	}
	ce.finalizarArchivo(true);						// Finalizo archivo, como guardo el estructurado junto, entonces param = true

	// Cierro el archivo original
	inFile.close();

	// Cierro y guardo los archivos temporales
	// BS
	tmpBSFile.close();

	// Estructurado
	tmpEstruc.pad();
	tmpEstrucFile.close();

	// Los abro de vuelta para guardarlos en la salida oficial
	ifstream tmpEstrucFile2("struct.tmp");
	ibitstream tmpEstruc2(&tmpEstrucFile2);

	// Archivo temporal de los indices del BS
	ifstream tmpBSFile2("BS.tmp");
	ibitstream tmpBS2(&tmpBSFile2);

	// Archivo final de salida
	ofstream outFile(outFileName);
	obitstream os(&outFile);

	// Guardo en el archivo de salida la cantidad de bloques
	outFile.write((char*)&acum, sizeof(acum));

	char tmp;

	// Append del BS
	tmpBSFile2.read(&tmp, sizeof(tmp));		// Leo del BS
	while(!(tmpBSFile2.eof())){
		outFile.write(&tmp, sizeof(tmp));			// Guardo en archivo final
		tmpBSFile2.read(&tmp, sizeof(tmp));		// Leo del BS
	}

	// Append del Estructurado
	tmpEstrucFile2.read(&tmp, sizeof(tmp));	// Leo del Estructurado
	while(!(tmpEstrucFile2.eof())){
		outFile.write(&tmp, sizeof(tmp));			// Guardo en archivo final
		tmpEstrucFile2.read(&tmp, sizeof(tmp));	// Leo del Estructurado
	}


	tmpEstrucFile2.close();
	tmpBSFile.close();

	outFile.close();

	delete the_mtf;

	cout << "¡¡¡Comprimido!!!" << endl;

	return 0;
}

int compresionMTF(string inFileName, string outFileName) {

	// Archivo temporal del estructurado
	ofstream tmpEstrucFile("MTF.tmp");
	obitstream tmpEstruc(&tmpEstrucFile);
	//CompresorEstructurado ce(&tmpEstruc);

	// Archivo temporal de los indices del BS
	ofstream tmpBSFile("BS.tmp");
	obitstream tmpBS(&tmpBSFile);

	uint acum = 0;
	streamsize s;
	uchar block[BLOCKSIZE];
	uint i = 0;

	mxf * the_mtf = new mtf();

	// Archivo de origen
	ifstream inFile(inFileName);
	ibitstream is(&inFile);

	while(inFile.good()) {

		inFile.read((char*)block, BLOCKSIZE);		// Leo el bloque del archivo
		acum++;										// Cuento la cantidad de bloques

		s = inFile.gcount();						// Obtengo el tamanio del bloque

		i = bs(block, s);							// BS, indice

		(*the_mtf)(block, s);						// MTF

		//os.write((char*)&i, sizeof(i));			// Guardo el indice del BS
		tmpBS.write((char*)&i, sizeof(i));			// Guardo el indice del BS en archivo temporal
		tmpEstruc.write((char*)block,s);
		//ce.comprimirBloque(block, s);				// Comprimo el bloque
		// ce.finalizarBloque();					//// No hace falta
	}
	//ce.finalizarArchivo(true);						// Finalizo archivo, como guardo el estructurado junto, entonces param = true

	// Cierro el archivo original
	inFile.close();

	// Cierro y guardo los archivos temporales
	// BS
	tmpBSFile.close();

	// Estructurado
	//tmpEstruc.pad();
	tmpEstrucFile.close();
/*
	// Los abro de vuelta para guardarlos en la salida oficial
	ifstream tmpEstrucFile2("struct.tmp");
	ibitstream tmpEstruc2(&tmpEstrucFile2);

	// Archivo temporal de los indices del BS
	ifstream tmpBSFile2("BS.tmp");
	ibitstream tmpBS2(&tmpBSFile2);

	// Archivo final de salida
	ofstream outFile(outFileName);
	obitstream os(&outFile);

	// Guardo en el archivo de salida la cantidad de bloques
	outFile.write((char*)&acum, sizeof(acum));

	char tmp;

	// Append del BS
	tmpBSFile2.read(&tmp, sizeof(tmp));		// Leo del BS
	while(!(tmpBSFile2.eof())){
		outFile.write(&tmp, sizeof(tmp));			// Guardo en archivo final
		tmpBSFile2.read(&tmp, sizeof(tmp));		// Leo del BS
	}

	// Append del Estructurado
	tmpEstrucFile2.read(&tmp, sizeof(tmp));	// Leo del Estructurado
	while(!(tmpEstrucFile2.eof())){
		outFile.write(&tmp, sizeof(tmp));			// Guardo en archivo final
		tmpEstrucFile2.read(&tmp, sizeof(tmp));	// Leo del Estructurado
	}


	tmpEstrucFile2.close();
	tmpBSFile.close();

	outFile.close();
*/
	delete the_mtf;

	cout << "¡¡¡Comprimido!!!" << endl;

	return 0;
}

int compresion(string filename) {

	ofstream archivo(filename);
	obitstream os(&archivo);

	CompresorEstructurado ce(&os);

	uchar a[BLOCKSIZE];
//	uchar* a = new uchar[BLOCKSIZE];

	a[0] = 15;
	a[1] = 8;
	a[2] = 1;
	a[3] = 1;
	a[4] = 99;
	a[5] = 15;
	a[6] = 8;
	a[7] = 1;
	a[8] = 1;
	a[9] = 99;
	a[10] = 15;
	a[11] = 8;
	a[12] = 1;
	a[13] = 1;
	a[14] = 99;

	ce.comprimirBloque(a,BLOCKSIZE);
	printf("Termino bloque: 1\n");

	a[0] = 0;
	a[1] = 150;
	a[2] = 30;
	a[3] = 10;
	a[4] = 1;
	a[5] = 15;
	a[6] = 8;
	a[7] = 1;
	a[8] = 1;
	a[9] = 99;
	a[10] = 15;
	a[11] = 8;
	a[12] = 1;
	a[13] = 1;
	a[14] = 99;

	ce.comprimirBloque(a,BLOCKSIZE);
	printf("Termino bloque: 2\n");

	a[0] = 1;
	a[1] = 1;
	a[2] = 1;
	a[3] = 1;
	a[4] = 20;
	a[5] = 15;
	a[6] = 8;
	a[7] = 1;
	a[8] = 1;

	ce.comprimirBloque(a,LASTBLOCKSIZE);
	printf("Termino bloque: 3\n");
	ce.finalizarArchivo(true);

	os.pad();
	archivo.close();

	//delete ce;
	//delete a;

	cout << "¡¡¡Comprimido!!!" << endl;
	return 0;

}

int descompresion(string filename){

	// descomprimir
	ifstream archivo(filename);
	ibitstream is(&archivo);

	DescompresorEstructurado de(&is);
	//uchar* b1 = new char[BLOCKSIZE];
	//uchar* b2 = new char[BLOCKSIZE];
	//uchar* b3 = new char[BLOCKSIZE];
	uchar b1[BLOCKSIZE];
	uchar b2[BLOCKSIZE];
	uchar b3[BLOCKSIZE];

	integer sizeBloque1,sizeBloque2,sizeBloque3;
	sizeBloque1 = de.descomprimir(b1,BLOCKSIZE);
	sizeBloque2 = de.descomprimir(b2,BLOCKSIZE);
	sizeBloque3 = de.descomprimir(b3,BLOCKSIZE);

	archivo.close();

	integer i;
	for(i=0;i<sizeBloque1;i++)
	{
		printf("%d\n",b1[i]);
	}
	printf("Fin Bloque 1, %d caracteres\n",sizeBloque1);
	for(i=0;i<sizeBloque2;i++)
	{
		printf("%d\n",b2[i]);
	}
	printf("Fin Bloque 2, %d caracteres\n",sizeBloque2);
	for(i=0;i<sizeBloque3;i++)
	{
		printf("%d\n",b3[i]);
	}
	printf("Fin Bloque 3, %d caracteres\n",sizeBloque3);


	//delete de;
	//delete[] b1;delete[] b2;delete[] b3;

	cout << "¡¡¡Descomprimido!!!" << endl;
	return 0;
}
